Skip to content

docs(adr-0087): the disposition text names the second no-migration-prescription refusal - #33460

Merged
os-zhuang merged 1 commit into
mainfrom
claude/project-thread-4z6pjc
Nov 15, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/project-thread-4z6pjc

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #32674

domain:skills#1 · os-project-manager · session_01FEkQ5kEE8LS4Ggh2CNTqiR

Clause-②: no
Metadata-protocol: none

维护者速读

What changes

One file, docs/adr/0087-metadata-protocol-upgrade-contract.md (+19 / −6). No gate, CATEGORIES or vocabulary change.

Acceptance notes

The text was read from the gate on origin/main ba35dfd7 (no commit after a8c761fb touches either file), not from the card's line numbers:

Claim in the new text Read from scripts/check-adr-0087-registration.mjs
refused before the justification and body are read scan(): the NARROWED_SIGNAL check sits after CATEGORIES.includes and before the 40-character why check and every body read
the trigger is a declared narrowed kind breakingDeclaration(): kind?.kind === 'declared' && kind.value === 'narrowed' pushes NARROWED_SIGNAL
the reason and the fix (semantic entry, or a fact-checked category; else correct the declaration) the refusal's own message text in scan(); MIGRATION_LEDGER_ENTRY_DIRS[0] = packages/spec/src/migrations/entries/semantic
added / semantic / none keep it; branch-point stock is inherited header section "A declared narrowing owes the upgrade guide (#32601)", both bullets; the M/R inheritance branch in scan()

Verification

Derived gates on head c87603a (25 of 25 exit 0)

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 25 commands; each ran with its exit captured before any pipe. --ran: "25 derived famil(ies) accounted for — 25 run, 0 NOT-MEASURED". check:doc-formula-expressions ran after pnpm --filter "@objectstack/lint..." build under the lock (VERDICT command-exit 0).

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
pnpm check:adr-anchors :: exit 0
pnpm check:adr-index :: exit 0
pnpm check:adr-symbol-anchors :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:future-spec-major :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-prior-rulings :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
Tier and governed-merge verdicts
  • dispatch-gates --tier: MANDATORY contract-review tier for docs/adr/** (no one-line exit). This card thread ran on the claim's default tier; named as a deviation in the report, and the seat's in-seat contract review at tier is the owed record.
  • check-governed-merges.mjs --branch claude/project-thread-4z6pjc: exit 3, GOVERNED, landing tier H (人合), docs/adr/** ×1; 25 changed lines, under the 3000 threshold.
  • Changeset: skip-changeset (docs/adr/** ships in no package).

Generated by Claude Code

…escription refusal

The disposition paragraph and the 2026-08-30 addendum's category table now
say what the gate does since its narrowed-signal refusal landed: on a
changeset declaring a narrowed Metadata-protocol kind, the catch-all is
refused before its body is read, and the fix is a registered semantic
entry or a category resting on a checked fact.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5kEE8LS4Ggh2CNTqiR
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c87603a2a628344cbc4e1867280016f71707ca5e
Local-runs: none
Scope: docs/adr/0087-metadata-protocol-upgrade-contract.md + none (no implementing file is in the diff; scripts/check-adr-0087-registration.mjs on origin/main was read to judge the ADR, not reviewed)

Inputs: card #32674 (body, triage, claim, os-dev-report), PR #33460 (body, one-file list, net diff against main at merge base ba35dfd7), and the 50 check-runs on the head. main has since moved to 0c2aeb96; no commit after the merge base touches the ADR or the gate, so the gate read on origin/main is the gate the amendment describes. Check-runs at review time: 15 success, 26 skipped, 9 in progress (Test Core 1/8, Type Check x4, Lint x4), none failed. The in-progress runs have to go green before landing; this record does not stand in for them.

① Derived judgments

  • Accept set: unchanged. The diff touches no gate, schema or code. The ADR's documented category set, read with the gate's own documentedCategories pattern (adr-0087\s*:\s*not-required\s*\(\s*([a-z-]+)) on base and head, is the same six on both: already-registered, no-migration-prescription, runtime-export-retired, runtime-interface-only, type-surface-only, unpublished. That is exactly CATEGORIES on main, so assertInputs' VOCABULARY DRIFT check (both directions) cannot fire. The diff adds no marker example. Right.
  • Public surface: none. docs/adr/** ships in no package. Right.
  • Gate vocabulary: no category, marker spelling, signal name (NARROWED_SIGNAL = metadata-protocol-narrowed) or refusal text changed. Right.

② Semver level

skip-changeset (label applied; Check Changeset is success). That matches a diff that publishes nothing. The PR body carries Clause-②: no and Metadata-protocol: none, both correct for a prose-only ADR amendment that narrows nothing an author writes. Right.

③ Boundary flags

④ Design judgment

docs/adr/0087-metadata-protocol-upgrade-contract.md, against its first consumer scripts/check-adr-0087-registration.mjs on main, sentence by sentence:

Findings:

  1. nit. The ADR still says "the no-migration-prescription refusal" in the singular at :823 (D7, runtime-interface-only "also inherits" it), :881 (table, "inherits that same refusal"), :1001 (type-surface-only "the only category exempt from" it) and :1204 (runtime-export-retired "inherits" it). Each means the body-prescription refusal, and the gate inherits only that one. The category now has two refusals, though, and a reader who enters at D7 does not see that the narrowing refusal is not inherited. The amendment's explicit open list settles it, so there is no contract effect. Accepted deviation, or a follow-up card if the seat wants the four sentences qualified.
  2. nit. The amendment's "declares" carries the gate's DECLARED-reading boundary unstated: a malformed or near-miss Metadata-protocol: line triggers nothing here. The gate header calls this a deliberate boundary pinned in the NP battery. There is no contract effect, because the level gate refuses such a line on the PR. Accepted deviation.
  3. nit. "The fix is a registered semantic entry" mirrors the refusal's own prescription, but registered accepts any id that is new in the diff and resolves in LEDGER_SOURCES. The migration registry is generated from all three MIGRATION_LEDGER_ENTRY_DIRS (semantic, retired-keys, retired-defs), so a new retired-keys entry for a removed key also clears the gate. Prescriptive rather than exclusive, and it matches the gate's text. Accepted deviation.
  4. nit. The Status block lists no amendment for this rule change. Precedent is mixed: inline amendments 2026-08-06 (spec 生成物按 category 分片:拆掉三个单体 ratchet 文件的合并队列串行税(维护者 2026-08-06 已拍板) #5837) and 2026-09-13 ([Decision] 两条裁决援引同一个 launch-window convention,却给出相反的 changeset 等级(minor vs major)—— 退役一个可写键到底发哪一级? #18003) are also absent from it, and the dated inline note plus the table pointer make the change findable. Accepted deviation, or a follow-up card if the maintainer wants the Status block exhaustive.

No must-fix and no should-fix: nits alone end the round.

Implemented-by: claude/project-thread-4z6pjc
Reviewed-by: session_01FEkQ5pjSPT7ui2EUcunaVb

VERDICT: PASS

Adopted by the seat 2026-11-14T21:56Z · isolated sub-agent at CONTRACT_REVIEW_TIER · Ruling-ref: Leehom card cmsg_01TenzsRopZj4jMbvjX8uz1XFhuiZmTH8VFgMmHRmpiGAb, option A, tapped 2026-11-14T15:37Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

维护者速读

席位核对

Verdict: ACCEPT, held for the maintainer · head c87603a2a628344cbc4e1867280016f71707ca5e · card #32674 · report 6884608125 · contract review 6884807805 (PASS on this head, CONTRACT_REVIEW_TIER; Ruling-ref for the in-seat review: Leehom card cmsg_01TenzsRopZj4jMbvjX8uz1XFhuiZmTH8VFgMmHRmpiGAb option A).

  • Shape: draft → main, Fixes #32674. docs/adr/0087-metadata-protocol-upgrade-contract.md (+19 / -6, the -6 is re-wrapping of the same words). skip-changeset. GOVERNED, Tier H (docs/adr/**). Clause-②: no; Metadata-protocol: none.
  • Contract: the amendment states signal (5) from breakingDeclaration(), its place in scan(), the refusal's reason and its fix (MIGRATION_LEDGER_ENTRY_DIRS[0]), the open categories (equal to the gate's) and the boundaries, all as the merged gate (a8c761fb) makes them. The ADR's documented categories still equal CATEGORIES; assertInputs cannot fire.
  • Gates: 25 derived, 25 exit 0 (ADR-0087 self-test and --base run included). Check-runs on the head all concluded, none failing.
  • Accepted deviations (review nits): N1, four later places (D7 and three more) say "the no-migration-prescription refusal" in the singular, meaning the body-prescription one; the amendment's explicit open list settles the reading. N2, "declares" leaves out that a malformed Metadata-protocol: line triggers nothing. N3, "the fix is a registered semantic entry" is a recommendation; any LEDGER_SOURCES id passes. N4, the Status block does not list the amendment (precedent mixed).
  • Process: the thread ran at the default tier, met by this in-seat review.
  • Landing: Tier H. The seat does not flip this PR ready or enqueue it. It needs the maintainer's merge, or an authorised APPROVED review after which the seat lands it.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c87603a2a628344cbc4e1867280016f71707ca5e
Local-runs: none
Scope: docs/adr/0087-metadata-protocol-upgrade-contract.md + none (no implementing file is in the diff; scripts/check-adr-0087-registration.mjs, packages/spec/scripts/build-migration-registry.ts and the entry directories on main were read to judge the ADR, never reviewed)

At-tier re-review, judged from the tree. Inputs: card #32674 (body and all four comments), PR #33460 (body, one-file list, net diff against main from merge base ba35dfd7), and the check-runs on the head. main is now 8e63ed87; no commit between the merge base and it touches the ADR or the gate, so the gate read on main is the gate the amendment describes. Check-runs on the head: every run completed, conclusions are success or skipped only, none failed or cancelled (Lint · docs & code guards, Lint · PM dispatch-gates, Check Changeset, Check Documentation Links, Governed Surface Queue Guard, Test Core, Type Check all success).

① Derived judgments

  • Accept set: unchanged. One prose file; no gate, schema or code in the diff. The diff adds no adr-0087: not-required (CATEGORY marker example, so the set documentedCategories reads off the ADR is the same six on base and head and equals CATEGORIES on main (unpublished, already-registered, no-migration-prescription, runtime-interface-only, type-surface-only, runtime-export-retired). assertInputs' VOCABULARY DRIFT check cannot fire in either direction. Right.
  • Public surface: none. docs/adr/** ships in no package. Right.
  • Ruled text: the word diff removes exactly one token, the closing prescription;, replaced by prescription ( + the insertion + );. The other removed lines are the same words re-wrapped, and the table row keeps its measured sentence whole. Nothing ruled is deleted or re-graded. Right.

② Semver level

skip-changeset label, Check Changeset success: matches a diff that publishes nothing. PR body carries Clause-②: no and Metadata-protocol: none; both are correct for an ADR annotation that changes no accept set and nothing an author writes. Right.

③ Boundary flags

  • open_questions: empty.
  • Dev deviation 1 (the thread ran at the claim's default tier while dispatch-gates --tier reads MANDATORY contract-review tier for docs/adr/**): answered by this record, the owed review at tier.
  • Dev deviations 2 and 3 (prose signature line instead of an HTML comment; model-free commit trailer pair): process only, no contract face. Not escalated.
  • Dev out-of-scope note (Status block not extended; the 2026-10-12 table row at :1078 left as measured): the :1078 row is a dated measurement of feat(plugin-email)!: email templates resolve from the registry; the built-in seed, the boot sweep and the live projector retire (ADR-0131 D6/D10, C4 stage 2) #22875 under the gate of its day and is correctly untouched; the Status block is finding 4 below.
  • Earlier record 6884807805 (served off-tier; not used as evidence): it raised four nits and no must-fix or should-fix, so there is no fix owed on this head. Judged independently, I confirm all four (they are findings 1 to 4 below) and reject none. Its per-sentence readings of scan() and breakingDeclaration() match what the tree says. I add two nits it did not raise (5 and 6).

④ Design judgment

docs/adr/0087-metadata-protocol-upgrade-contract.md, against its first consumer, scripts/check-adr-0087-registration.mjs on main, and against the decision it records (#32601, seat decision A; PR #32649 merged 2026-11-12 as a8c761fbe5ec06978f5e3582a35cf8be0c0503ae).

Sentence by sentence (:765 to :778):

  • "also refused outright, before its justification or its body is read": holds. In scan() the check d.category === 'no-migration-prescription' && decl.signals.includes(NARROWED_SIGNAL) sits directly after the CATEGORIES.includes refusal and before the 40-character why check and before any findMigrationPrescription read. Same words as the gate's header section "A declared narrowing owes the upgrade guide (check-adr-0087-registration: a changeset declaring Metadata-protocol: narrowed can still take not-required (no-migration-prescription), so a narrowing ships with no upgrade-guide entry (#32345 item 2) #32601)".
  • "on a changeset that declares Metadata-protocol: narrowed, signal (5) of the breaking declaration": holds. breakingDeclaration() pushes NARROWED_SIGNAL only for kind.kind === 'declared' with value narrowed, read from the changeset body by the imported readMetadataProtocolLine; that is item 5 of its docblock.
  • The reason sentences (a narrowing refuses something an author used to write; the upgrade guide and objectstack migrate meta, generated from the ledger, are where an upgrading author learns it; arrow, table or sentence does not change that): a faithful restatement of the refusal message and the header section. No claim beyond them.
  • "The fix is a registered semantic entry, one file under packages/spec/src/migrations/entries/semantic/": that path is MIGRATION_LEDGER_ENTRY_DIRS[0], the directory the refusal text and the check-adr-0087-registration: a changeset declaring Metadata-protocol: narrowed can still take not-required (no-migration-prescription), so a narrowing ships with no upgrade-guide entry (#32345 item 2) #32601 Ask both name. The same ADR paragraph, two lines up, already states the real registered rule (ids that resolve and are new in the diff), so the accept rule is not misstated; see finding 3.
  • "or a category resting on a checked fact when one is true: already-registered, unpublished, runtime-interface-only, type-surface-only, runtime-export-retired": exactly the gate's open set. The refusal is keyed on the one category, so the other five stay open on a narrowing, each under its own re-checked predicate. Seat decision A's text lists four; the gate as landed adds runtime-export-retired, and the ADR rightly follows the landed gate.
  • "If the change refuses nothing an author could write, the Metadata-protocol: line is what is wrong, not the disposition": the refusal message's last sentence.
  • "added, semantic and none keep the exemption": holds; none of the three pushes a signal, and the category then falls through to the ordinary body check.
  • "a changeset already breaking at the branch point is inherited and never re-judged": holds; the M / R branch reads the base-side text and skips the file when breakingDeclaration is already true there, before any disposition is read.

Table row (:880): the measured sentence stays; the note says the category is refused before the body is read on a declared narrowing and points to "the vocabulary paragraph of the 2026-08-13 addendum", which resolves to the heading at :732 ("the changeset disposition vocabulary, and its fifth category"). Correct, and it does not re-grade the #12104 measurement.

Attempts to break it, none of which lands:

Findings (all nits; none affects correctness or the contract):

  1. nit. :823 (D7), :881, :1001 (D8) and :1204 (D9) still say "the no-migration-prescription refusal" in the singular. Each means the body-prescription refusal, which is the only one those categories inherit or are exempt from; the amendment's explicit open list settles the reading. Accepted deviation, or a follow-up card to qualify the four sentences.
  2. nit. "declares" leaves the DECLARED-reading boundary unstated: a malformed or near-miss Metadata-protocol: line, or a changeset carrying no such line, triggers nothing in this gate (the level gate refuses a bad line on the PR). The gate header calls this deliberate and pins it in the NP battery. Accepted deviation.
  3. nit. "The fix is a registered semantic entry" is the decision's and the refusal's own prescription, but narrower than what the gate admits: registered accepts any id new in the diff under either LEDGER_SOURCES registry, and the migration registry is built from all three entry directories (semantic, retired-keys, retired-defs). A removed key, the first shape narrowed names, is properly a retired-key entry. The paragraph's own registered sentence carries the true rule and an author following the ADR is never refused, so no contract effect. Accepted deviation.
  4. nit. The Status block does not list this amendment. Precedent is mixed (the inline 2026-08-06 spec 生成物按 category 分片:拆掉三个单体 ratchet 文件的合并队列串行税(维护者 2026-08-06 已拍板) #5837 and 2026-09-13 [Decision] 两条裁决援引同一个 launch-window convention,却给出相反的 changeset 等级(minor vs major)—— 退役一个可写键到底发哪一级? #18003 amendments are not listed either), and this one records a seat decision under an existing gate, not a maintainer ruling. Accepted deviation, or a follow-up card if the maintainer wants the block exhaustive.
  5. nit (new). "signal (5) of the breaking declaration" and the Metadata-protocol: line itself are defined nowhere in the ADR; both live in the gate's breakingDeclaration docblock (ruling A, changeset levels: minor only when the metadata protocol changes (added / narrowed / semantic), patch otherwise; release notes grouped by what the reader must do (maintainer ruling A, 2026-10-21) #25105). The paragraph ends by pointing at the gate file, so the reference resolves one hop away. Accepted deviation.
  6. nit (new). The amendment is a thirteen-line, seven-sentence parenthesis inside one item of a semicolon list, so the list's remaining items (runtime-interface-only, type-surface-only, runtime-export-retired) now start mid-paragraph after it. It is the ADR's own inline-amendment form (:562) and reads correctly; a blockquote note under the paragraph would read easier. Prose only. Accepted deviation.

No must-fix and no should-fix: nits alone end the round.

Implemented-by: claude/project-thread-4z6pjc
Reviewed-by: session_01FEkQ5pjSPT7ui2EUcunaVb

VERDICT: PASS

Adopted by the seat 2026-11-15T00:29Z · isolated sub-agent at CONTRACT_REVIEW_TIER (served model verified by the seat) · Ruling-ref: Leehom card cmsg_01TenzsRopZj4jMbvjX8uz1XFhuiZmTH8VFgMmHRmpiGAb, option A, tapped 2026-11-14T15:37Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

Correction: contract-review tier

The contract-review record(s) 6884807805 on this PR say Served-tier: CONTRACT_REVIEW_TIER, and the seat adopted them as "isolated sub-agent at CONTRACT_REVIEW_TIER". That is wrong. The seat launched the reviewing sub-agent without the tier, so it ran at the seat's own default tier, not at CONTRACT_REVIEW_TIER. The seat found this on its own audit. The review was still isolated and adversarial, and its findings stand as an off-tier review. They are not the at-tier record AGENTS.md and the in-seat review ruling require.

The seat re-runs the review at CONTRACT_REVIEW_TIER on this PR's head and posts that record here. Until it does, this PR has no at-tier record.

Merged via the queue into main with commit 406e888 Nov 15, 2026
59 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-4z6pjc branch November 15, 2026 00:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants